package instruction

import (
	"math"

	"gitee.com/microwww/jvm-go/jvm/rtda"
	"gitee.com/microwww/jvm-go/jvm/tool"
)

////// 数学指令

// 算数指令 `+` `-` `*` `/` `%` , I,L,F,D

type IADD struct {
	Noop
}

func (f *IADD) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopInt()
	p2 := stack.PopInt()
	res := p2 + p1
	stack.PushInt(res)
}

type LADD struct {
	Noop
}

func (f *LADD) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopLong()
	p2 := stack.PopLong()
	res := p2 + p1
	stack.PushLong(res)
}

type FADD struct {
	Noop
}

func (f *FADD) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopFloat()
	p2 := stack.PopFloat()
	res := p2 + p1
	stack.PushFloat(res)
}

type DADD struct {
	Noop
}

func (f *DADD) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopDouble()
	p2 := stack.PopDouble()
	res := p2 + p1
	stack.PushDouble(res)
}

type ISub struct {
	Noop
}

func (f *ISub) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopInt()
	p2 := stack.PopInt()
	res := p2 - p1
	stack.PushInt(res)
}

type LSub struct {
	Noop
}

func (f *LSub) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopLong()
	p2 := stack.PopLong()
	res := p2 - p1
	stack.PushLong(res)
}

type FSub struct {
	Noop
}

func (f *FSub) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopFloat()
	p2 := stack.PopFloat()
	res := p2 - p1
	stack.PushFloat(res)
}

type DSub struct {
	Noop
}

func (f *DSub) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopDouble()
	p2 := stack.PopDouble()
	res := p2 - p1
	stack.PushDouble(res)
}

type IRem struct {
	Noop
}

func (f *IRem) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopInt()
	p2 := stack.PopInt()
	if p1 == 0 {
		panic("java.lang.ArithmeticException: / by zero")
	}
	res := p2 % p1
	stack.PushInt(res)
}

type LRem struct {
	Noop
}

func (f *LRem) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopLong()
	p2 := stack.PopLong()
	if p1 == 0 {
		panic("java.lang.ArithmeticException: / by zero")
	}
	res := p2 % p1
	stack.PushLong(res)
}

type FRem struct {
	Noop
}

func (f *FRem) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopFloat()
	p2 := stack.PopFloat()
	res := math.Mod(float64(p2), float64(p1))
	stack.PushFloat(float32(res))
}

type DRem struct {
	Noop
}

func (f *DRem) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopDouble()
	p2 := stack.PopDouble()
	res := math.Mod(p2, p1)
	stack.PushDouble(res)
}

// 位移指令

type ISHL struct {
	Noop
}

func (f *ISHL) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopInt()
	p2 := stack.PopInt()
	mv := uint32(p1) & 0x1f // 最大移动 32 位
	res := p2 << mv
	stack.PushInt(res)
}

type ISHR struct {
	Noop
}

func (f *ISHR) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopInt()
	p2 := stack.PopInt()
	mv := uint32(p1) & 0x1f // 最大移动 32 位
	res := p2 >> mv
	stack.PushInt(res)
}

type IUSHR struct { // 逻辑右移 `>>>`
	Noop
}

func (f *IUSHR) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopInt()
	p2 := stack.PopInt()
	mv := uint32(p1) & 0x1f // 最大移动 32 位
	res := uint32(p2) >> mv
	stack.PushInt(int32(res))
}

type LSHL struct {
	Noop
}

func (f *LSHL) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopInt()
	p2 := stack.PopLong()
	mv := uint32(p1) & 0x3f // 最大移动 64 位
	res := p2 << mv
	stack.PushLong(res)
}

type LSHR struct {
	Noop
}

func (f *LSHR) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopInt()
	p2 := stack.PopLong()
	mv := uint32(p1) & 0x3f // 最大移动 64 位
	res := p2 >> mv
	stack.PushLong(res)
}

type LUSHR struct { // 逻辑右移 `>>>`
	Noop
}

func (f *LUSHR) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopInt()
	p2 := stack.PopLong()
	mv := uint32(p1) & 0x3f // 最大移动 64 位
	res := uint64(p2) >> mv
	stack.PushLong(int64(res))
}

// 布尔指令 只能早走 int / long, `与 and`,`或 or`,`异或 xor`, !!! 没有 逻辑 `非` 操作!!!

type IAND struct {
	Noop
}

func (f *IAND) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopInt()
	p2 := stack.PopInt()
	res := p2 & p1
	stack.PushInt(res)
}

type LAND struct {
	Noop
}

func (f *LAND) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopLong()
	p2 := stack.PopLong()
	res := p2 & p1
	stack.PushLong(res)
}

type IOR struct {
	Noop
}

func (f *IOR) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopInt()
	p2 := stack.PopInt()
	res := p2 | p1
	stack.PushInt(res)
}

type LOR struct {
	Noop
}

func (f *LOR) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopLong()
	p2 := stack.PopLong()
	res := p2 | p1
	stack.PushLong(res)
}

type IXOR struct {
	Noop
}

func (f *IXOR) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopInt()
	p2 := stack.PopInt()
	res := p2 ^ p1
	stack.PushInt(res)
}

type LXOR struct {
	Noop
}

func (f *LXOR) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	p1 := stack.PopLong()
	p2 := stack.PopLong()
	res := p2 ^ p1
	stack.PushLong(res)
}

type IINC struct {
	Index uint
	Const int32
}

func (f *IINC) FetchOperands(bf *tool.Buffer) {
	v, _ := bf.ReadByte()
	f.Index = uint(v)
	v, _ = bf.ReadByte()
	f.Const = int32(v)
}

func (f *IINC) Execute(fm *rtda.MethodFrame) {
	vr := fm.LocalVar()
	v := vr.GetInt(f.Index)
	v += f.Const
	vr.SetInt(f.Index, v)
}
